iT邦幫忙

2025 iThome 鐵人賽

DAY 1
0
Rust

把前端加速到天花板:Rust+WASM 即插即用外掛系列 第 1

Day 0|預備知識與開發環境

  • 分享至 

  • xImage
  •  
痾其實我也不知道會不會完賽,升上研究所之後變得好忙...

在寫套件之前,我們要知道自己要把什麼東西「造出來」、用什麼工具「把它運到瀏覽器」,以及前端如何「優雅地使用它」。所以我不會先講很多 Rust 語法;先理解輸出形態與 toolchain,之後所有選擇都會明朗而省時。

為什麼先從輸出形態開始

Rust 要跑進瀏覽器,最終必須變成 WebAssembly,並透過一層 JavaScript glue 被前端使用。這裡有三個面向需要去釐清:

  1. 產物長相

    你的 crate 會輸出成一個 .wasm 檔與相對應的 JS shim(由 wasm-bindgen 生成),還可能附帶 .d.ts 型別定義。

  2. 如何被前端載入

    現代前端以 ESM 為主。你需要讓 bundler 或 runtime 知道要如何 import 你的封裝、去哪裡找到 .wasm、要不要在 Web Worker 跑。

  3. 封裝與發佈

    面向 npm 的套件,要處理 package.json 的 exports map、型別檔、瀏覽器與 Node 的差異入口,以及效能/體積的平衡。

把這三件事想清楚,後面的 lib 與 cdylib、workspace、wasm-pack、Vite/Next 只是各自站在不同關卡的小幫手。

Toolchain 最簡心智圖

Rust Code → 編成 WebAssembly → 由 wasm-bindgen 生成 JS/TS API → 交給前端 bundler 或 runtime 透過 ESM 載入與使用。

Rust crate (lib)
  └─ cargo build --target wasm32-unknown-unknown
       └─ .wasm 產物
            └─ wasm-bindgen/wasm-pack 生成 JS glue + d.ts
                 └─ 前端 (Vite/Next) 以 ESM import
                      └─ 瀏覽器 / Web Worker 執行

一些名詞介紹(Rust ↔ 前端)

  • crate:Rust 的包;類似 npm package 的原始碼單位,但發佈與編譯由 Cargo 管控。
  • workspace:多 crate 的專案集合;前端常見的 monorepo 類似概念。
  • lib / cdylib:lib 是一般 Rust 函式庫;cdylib 用於產出跨語言動態庫(對 wasm/bindgen 的目標更直接)。
  • wasm-bindgen:把 Rust 暴露的函式/型別轉成 JS 可呼叫的介面工具,生成 glue code 與型別。
  • wasm-pack:封裝 wasm-bindgen 流程,幫你做成 npm 套件結構,可直接發佈或本地連結。
  • ESM:前端與 Node 新世代的模組標準,以 import/export 為主。
  • Web Worker:在瀏覽器開另一條執行緒跑計算的機制,避免阻塞主緒。
  • exports map:package.json 的映射表,定義不同環境與路徑的入口,確保 import 與型別解析正確。
  • d.ts:TypeScript 型別宣告檔,讓 JS 套件也能有良好補全與型別安全。

我們要安裝什麼

  • rustup 與 cargo:Rust toolchain 管理與建置工具。
  • wasm-pack:把你的 crate 包成 npm 友善的 wasm 套件。
  • Node.js 與 pnpm:前端與封裝工具。
  • Vite 或 Next.js:讓瀏覽器 demo、範例站與真實應用跑起來。
  • VS Code 與 rust-analyzer:對 Rust 友善的開發體驗與語言伺服器。

檢查命令

rustup --version
cargo --version
rustup target list --installed
wasm-pack --version
node -v
pnpm -v

建議目標平台安裝

rustup target add wasm32-unknown-unknow

知道到這裡即可下面是一些更詳細的講解(-0-)


lib 與 cdylib:

lib 與 cdylib 的差別:

  • lib:寫純 Rust 函式庫,本身不負責對外語言邊界,單元測試方便。
  • cdylib:為跨語言使用而生的產出型態,聚焦「可被其他生態載入」。對 wasm 來說,就像是「這個我要給 JS 用」的明確聲明。

這邊我會建議用 workspace 分成 core 與 wasm 兩個 crate:core(lib)純邏輯、零平台耦合、可複用、可測試。然後 wasm(cdylib)專責與 JS 溝通(如 wasm-bindgen 標註、型別包裝、錯誤轉譯)。這樣做可以把演算法和平台邊界解耦,架構會更清楚明瞭。

wasm-bindgen 跟 wasm-pack:

wasm-bindgen 跟 wasm-pack 分別是什麼:

  • wasm-bindgen:你在 Rust 函式或型別上加標註,讓工具知道哪些要暴露給 JS。它輸出 JS glue、處理字串、陣列、物件的來回轉換。
  • wasm-pack:把 wasm-bindgen 的輸出打包成 npm 套件骨架,幫你生成 package.json、型別、檔案結構,甚至支援發佈。

常見工作流程

  1. 在 wasm crate 裡標註要給 JS 用的 API。
  2. 使用 wasm-pack build 產生 pkg/ 資料夾(含 .wasm、.js、.d.ts)。
  3. 在前端專案 import pkg 來使用。

ESM、exports map 與 d.ts

前端體驗的關鍵在 package.json 的設計:

  • type 設為 module,說明使用 ESM。
  • exports map 指定主要入口(.js)與 .wasm 資源的相對應路徑。
  • types 指到對應的 .d.ts,讓 TS/IDE 有正確補全。
  • 若同時支援 Node 与 瀏覽器,可用 exports map 區分條件(如 import 與 require、browser 與 default),避免使用者踩坑。

為什麼重要?如果沒有 exports map,使用者的 bundler 可能找不到真正入口或把內部檔案誤當公開 API;沒有 d.ts,TS user experience 會大打折扣;而沒有明確 ESM,會在 Node 與瀏覽器間出現相容問題。

那主緒跟 Worker 的 Worker 是啥

當你的運算很重、會阻塞 UI 主緒時,就把 wasm 跑到 Worker,而我們要做的套件運算都偏大所以會用到 worker。

所以是這樣安排的:

  • 主緒:負責 UI、事件、動畫。
  • Worker:專注計算,透過 postMessage 與主緒交換資料。

開發流程箭頭圖(從 Rust 到前端)

[crate: core (lib)]      [crate: wasm (cdylib + wasm-bindgen)]
        │                           │
        └──      演算法/邏輯  ────►   │  API、錯誤轉譯
                                      │
                         cargo build --target wasm32-unknown-unknown
                                      │
                               wasm-bindgen / wasm-pack
                                      │
                     生成 pkg/: index.js + package.json + .wasm + .d.ts
                                      │
                            前端 (Vite/Next) 以 ESM import
                                      │
                      瀏覽器主緒 或 Web Worker 實際執行與渲染

Today’s Recap

今天釐清了一些名詞與基本概念,名詞如下:crate、workspace、lib/cdylib、wasm-bindgen、wasm-pack、ESM、Web Worker、exports map、d.ts 。以下是三個小問題,希望讓大家檢驗有沒有在文章內學到東西:

  1. lib 與 cdylib 的差別

    lib 是一般函式庫,偏邏輯內聚;cdylib 用於跨語言邊界的產物,搭 wasm-bindgen 才能讓 JS 直接用。實務上常採 lib + cdylib 分層:lib 放核心邏輯、cdylib 專注 JS 介面。

  2. workspace 的用途

    把多個 crate 放在一個倉庫中共享版本、鎖檔與工具鏈:可把核心邏輯與平台邊界切開;之後你想同時輸出「瀏覽器版 wasm」和「Node 原生擴充」也更好管理。

  3. exports map 為何重要

    它決定「使用者 import 你的套件時」實際拿到哪個檔案、在哪個環境下走哪個入口,並避免外部依賴你的內部檔案路徑。這是維持 API 穩定與 DX 的關鍵。


在規劃的時候覺得還好,真的寫出來快瘋掉,好好笑不能確保之後的內容還會這麼多真的是在搞死自己,唸的人也很痛苦我很抱歉。


下一篇
Day 1|MVP(官方 Hello World + Vite)
系列文
把前端加速到天花板:Rust+WASM 即插即用外掛3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言